Python linux进程及线程

threading多线程

(1)默认启动的线程的为非守护线程,也就是意味着daemon=False
(2)守护线程:主线程退出子线程一起退出,不管是否执行完毕,由主线程执行exit_group系统调用在同一个线程组的线程全部杀死;主进程start()后不阻塞
(3)非守护线程:主线程退出子线程不退出,但是实际上会调用join去处理等待子线程退出后主线程才会真正退出,由主线程执行exit_group系统调用在同一个
线程组的线程全部杀死;主进程start()后不阻塞

用户态多线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
import threading
import multiprocessing
import time
import signal
import sys
import datetime
import os


'''
threading多线程:
(1)默认启动的线程的为非守护线程,也就是意味着daemon=False
(2)守护线程:主线程退出子线程一起退出,不管是否执行完毕,由主线程执行exit_group系统调用在同一个线程组的线程全部杀死;主进程start()后不阻塞
(3)非守护线程:主线程退出子线程不退出,但是实际上会调用join去处理等待子线程退出后主线程才会真正退出,由主线程执行exit_group系统调用在同一个
线程组的线程全部杀死;主进程start()后不阻塞
_main_thread = _MainThread()

def _shutdown():
# Obscure: other threads may be waiting to join _main_thread. That's
# dubious, but some code does it. We can't wait for C code to release
# the main thread's tstate_lock - that won't happen until the interpreter
# is nearly dead. So we release it here. Note that just calling _stop()
# isn't enough: other threads may already be waiting on _tstate_lock.
tlock = _main_thread._tstate_lock
# The main thread isn't finished yet, so its thread state lock can't have
# been released.
assert tlock is not None
assert tlock.locked()
tlock.release()
_main_thread._stop()
t = _pickSomeNonDaemonThread() #找到所有非Daemon状态的线程
while t:
t.join() #等待所有非Daemon状态的线程结束
t = _pickSomeNonDaemonThread()
_main_thread._delete()

(4)join的作用是让主线程等待子线程执行结束,并正常回收资源

multiprocessing多进程:
(1)默认启动的进程的为非守护进程,也就是意味着daemon=False
(2)守护进程:父进程退出子进程一起退出,不管是否执行完毕;主进程start()后不阻塞;不允许有子进程;
(3)非守护进程:主进程退出子进程不退出,但是实际上父进程调用wait4等待子进程;主进程start()后不阻塞;
进程退出都是由_exit库函数实现的,该机制让内核回收资源,该系统调用会调用exit_group系统调用

注意的是从内核的角度看,用户态的线程本质上还是一个进程,对于同一个进程(用户态角度)中不同的线程其tgid是相同的,但是pid各不相同。
主线程即group_leader(主线程会创建其他所有的子线程)

SYSCALL_DEFINE1(exit_group, int, error_code)
{
do_group_exit((error_code & 0xff) << 8);
/* NOTREACHED */
return 0;
}

/*
* Take down every thread in the group. This is called by fatal signals
* as well as by sys_exit_group (below).
*/
void
do_group_exit(int exit_code)
{
struct signal_struct *sig = current->signal;

BUG_ON(exit_code & 0x80); /* core dumps don't get here */
/*
检查current->sig->flags的SIGNAL_GROUP_EXIT标志是否置位
或者current->sig->group_exit_task是否不为NULL
*/
if (signal_group_exit(sig))
exit_code = sig->group_exit_code; /* group_exit_code存放的是线程组终止代码 */
else if (!thread_group_empty(current)) { /* 检查线程组链表是否不为空,线程组leader非空*/
struct sighand_struct *const sighand = current->sighand;

spin_lock_irq(&sighand->siglock);
if (signal_group_exit(sig))
/* Another thread got here before we took the lock. */
exit_code = sig->group_exit_code;
else {
sig->group_exit_code = exit_code;
sig->flags = SIGNAL_GROUP_EXIT;
zap_other_threads(current); /* 遍历整个线程组链表,并杀死其中的每个线程 */
}
spin_unlock_irq(&sighand->siglock);
}

do_exit(exit_code);
/* NOTREACHED */
}

do_group_exit的处理方式:调用clone会通过_exit调用exit_group
1. 用户态多线程程序,子线程退出,不会调用do_group_exit
2. 用户态多线程程序,父线程退出,会调用do_group_exit,处理父进程线程组内的线程
3. 用户态多进程程序,子进程退出,会调用do_group_exit,处理子进程线程组内的线程
4. 用户态多进程程序,父进程退出,会调用do_group_exit,处理父进程进程组内的进程


(4)join的作用是让父进程等待子进程执行结束,并正常回收资源,否则存在僵尸进程

libc _exit函数调用:
(1)exit_group的调用被封装在_exit函数,会在exit系统调用前面调用
void
_exit (int status)
{
while (1)
{
#ifdef __NR_exit_group
INLINE_SYSCALL (exit_group, 1, status);
#endif
INLINE_SYSCALL (exit, 1, status);

#ifdef ABORT_INSTRUCTION
ABORT_INSTRUCTION;
#endif
}
}
libc_hidden_def (_exit)
rtld_hidden_def (_exit)
weak_alias (_exit, _Exit)
(2)通过clone的汇编代码,clone子线程调用线程函数后会调用_exit返回函数返回值
ENTRY (BP_SYM (__clone))
/* Sanity check arguments. */
movq $-EINVAL,%rax
testq %rdi,%rdi /* no NULL function pointers */
jz SYSCALL_ERROR_LABEL
testq %rsi,%rsi /* no NULL stack pointers */
jz SYSCALL_ERROR_LABEL

/* Insert the argument onto the new stack. */
subq $16,%rsi
movq %rcx,8(%rsi)

/* Save the function pointer. It will be popped off in the
child in the ebx frobbing below. */
movq %rdi,0(%rsi)

/* Do the system call. */
movq %rdx, %rdi
movq %r8, %rdx
movq %r9, %r8
mov 8(%rsp), %R10_LP
movl $SYS_ify(clone),%eax

/* End FDE now, because in the child the unwind info will be
wrong. */
cfi_endproc;
syscall

testq %rax,%rax
jl SYSCALL_ERROR_LABEL
jz L(thread_start)

L(pseudo_end):
ret

L(thread_start):
cfi_startproc;
/* Clearing frame pointer is insufficient, use CFI. */
cfi_undefined (rip);
/* Clear the frame pointer. The ABI suggests this be done, to mark
the outermost frame obviously. */
xorl %ebp, %ebp

#ifdef RESET_PID
testq $CLONE_THREAD, %rdi
jne 1f
testq $CLONE_VM, %rdi
movl $-1, %eax
jne 2f
movl $SYS_ify(getpid), %eax
syscall
2: movl %eax, %fs:PID
movl %eax, %fs:TID
1:
#endif

/* Set up arguments for the function call. */
popq %rax /* Function to call. */
popq %rdi /* Argument. */
call *%rax
/* Call exit with return value from function call. */
movq %rax, %rdi
call HIDDEN_JUMPTARGET (_exit)
cfi_endproc;

cfi_startproc;

clone:创建线程/进程, CLONE_THREAD标记
(1)If CLONE_THREAD is set, the child is placed in the same thread
group as the calling process. To make the remainder of the
discussion of CLONE_THREAD more readable, the term "thread" is
used to refer to the processes within a thread group.

Thread groups were a feature added in Linux 2.4 to support the
POSIX threads notion of a set of threads that share a single
PID. Internally, this shared PID is the so-called thread
group identifier (TGID) for the thread group. Since Linux
2.4, calls to getpid(2) return the TGID of the caller.

The threads within a group can be distinguished by their
(system-wide) unique thread IDs (TID). A new thread's TID is
available as the function result returned to the caller of
clone(), and a thread can obtain its own TID using gettid(2).

(2)When a call is made to clone() without specifying
CLONE_THREAD, then the resulting thread is placed in a new
thread group whose TGID is the same as the thread's TID. This
thread is the leader of the new thread group.
'''

def clild_thread_target():
print("I am chlid thread, process gid ", os.getpgid(os.getpid()))
time.sleep(1)
print("I am chlid thread finish")

def test1_clild_process_target():
t1 = threading.Thread(target=clild_thread_target, daemon=False) # default
t1.start()
print("chlid thread isdeamon status : %s " % str(t1.isDaemon()))
print("chlid process gid : ", os.getpgid(os.getpid()))

def test2_clild_process_target():
t1 = threading.Thread(target=clild_thread_target, daemon=True)
t1.start()
print("chlid thread isdeamon status : %s " % str(t1.isDaemon()))
print("chlid process gid : ", os.getpgid(os.getpid()))


def test3_clild_process_target():
t1 = threading.Thread(target=clild_thread_target, daemon=False) # default
t1.start()
print("chlid thread isdeamon status : %s " % str(t1.isDaemon()))
print("chlid process gid : ", os.getpgid(os.getpid()))
t1.join()
time.sleep(1)


def test4_clild_process_target():
t1 = threading.Thread(target=clild_thread_target, daemon=True)
t1.start()
print("chlid thread isdeamon status : %s " % str(t1.isDaemon()))
print("chlid process gid : ", os.getpgid(os.getpid()))
t1.join()

def test5_term_signle_handler(signum, frame):
print('I received: ', signum)
return

def test5_clild_process_target():
signal.signal(signal.SIGTERM, test5_term_signle_handler)
t1 = threading.Thread(target=clild_thread_target, daemon=True)
t1.start()
print("chlid thread isdeamon status : %s " % str(t1.isDaemon()))
print("chlid process gid : ", os.getpgid(os.getpid()))

signal.pause()
sys.exit(0)

def test6_clild_process_target():
print("chlid process gid : ", os.getpgid(os.getpid()))
time.sleep(10)


def subprocess_new_thread():
clild_process = \
multiprocessing.Process(target=test6_clild_process_target, daemon=False)
clild_process.start()
print("chlid process deamon status : %s " % str(clild_process.daemon))
# time.sleep(3)
# clild_process.terminate()
# clild_process.join()
print("chlid process join")

time.sleep(1)

def mainprocess_new_thread():
test2_clild_process_target()
time.sleep(10)
print("main process join")


if __name__ == "__main__":
print("Now, %s, process gid %d" % (datetime.datetime.now(), os.getpgid(os.getpid())))
subprocess_new_thread()
-------------本文结束感谢您的阅读-------------
0%